package reesoft.common.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPubSub;

import java.util.*;

/**
 * 公用 Jedis 缓存处理单元
 *
 * Created by reesoft on 2017-07-14
 */
public class JedisUtil
{
    private static final Logger LOG = LoggerFactory.getLogger(JedisUtil.class);

    // 连接池
    private static final JedisPool jedisPool = ApplicationContextUtil.getBean(JedisPool.class);

    /**
     * 从缓存获取指定键的值
     *
     * @param key 键
     * @return 值
     */
    public static String get(final String key)
    {
        String value = null;
        Jedis jedis = null;

        try
        {
            jedis = getResource();
            value = jedis.get(key);

            // 尽早释放连接
            returnResource(jedis);
            jedis = null;

            value = !StringUtil.isBlank(value) && !"nil".equalsIgnoreCase(value) ? value : null;
        }
        catch (Exception e)
        {
            LOG.warn("get exception, key:{}", key, e);
        }
        finally
        {
            returnResource(jedis);
        }

        return value;
    }

    /**
     * 把指定键值存储到缓存中
     *
     * @param key               键
     * @param value             值
     * @param cacheMilliSeconds 过期时间，单位：毫秒，0 为不过期
     * @return 存储是否成功
     */
    public static boolean set(final String key, final String value, final long cacheMilliSeconds)
    {
        String result = null;
        Jedis jedis = null;

        try
        {
            jedis = getResource();

            if (cacheMilliSeconds != 0)
            {
                result = jedis.psetex(key, cacheMilliSeconds, value);
            }
            else
            {
                result = jedis.set(key, value);
            }
        }
        catch (Exception e)
        {
            LOG.warn("set exception, key:{}", key, e);
        }
        finally
        {
            returnResource(jedis);
        }

        return "OK".equals(result);
    }

    /**
     * 从缓存获取列表
     *
     * @param key 键
     * @return 列表对象
     */
    public static List<String> getList(final String key)
    {
        List<String> value = null;
        Jedis jedis = null;

        try
        {
            jedis = getResource();
            value = jedis.lrange(key, 0, -1);
        }
        catch (Exception e)
        {
            LOG.warn("getList exception, key:{}", key, e);
        }
        finally
        {
            returnResource(jedis);
        }

        return value;
    }

    /**
     * 把列表存储到缓存中
     *
     * @param key          键
     * @param value        值
     * @param cacheSeconds 过期时间，0 为不过期
     * @return 存储到缓存的列表元素数量
     */
    public static long setList(final String key, final List<String> value, final int cacheSeconds)
    {
        long result = 0;
        Jedis jedis = null;

        try
        {
            jedis = getResource();

            jedis.del(key);

            result = jedis.rpush(key, (String[]) value.toArray());

            if (cacheSeconds != 0)
            {
                jedis.expire(key, cacheSeconds);
            }
        }
        catch (Exception e)
        {
            LOG.warn("setList exception, key:{}", key, e);
        }
        finally
        {
            returnResource(jedis);
        }

        return result;
    }

    /**
     * 向缓存中的列表添加值
     *
     * @param key   键
     * @param value 值
     * @return 添加到缓存的列表元素数量
     */
    public static long addToList(final String key, final String... value)
    {
        long result = 0;
        Jedis jedis = null;

        try
        {
            jedis = getResource();
            result = jedis.rpush(key, value);
        }
        catch (Exception e)
        {
            LOG.warn("addToList exception, key:{}", key, e);
        }
        finally
        {
            returnResource(jedis);
        }

        return result;
    }

    /**
     * 从缓存获取集合
     *
     * @param key 键
     * @return 值
     */
    public static Set<String> getSet(final String key)
    {
        Set<String> value = null;
        Jedis jedis = null;

        try
        {
            jedis = getResource();
            value = jedis.smembers(key);
        }
        catch (Exception e)
        {
            LOG.warn("getSet exception, key:{}", key, e);
        }
        finally
        {
            returnResource(jedis);
        }

        return value;
    }

    /**
     * 把集合存储到缓存中
     *
     * @param key          键
     * @param value        值
     * @param cacheSeconds 超时时间，0为不超时
     * @return 存储到缓存的元素数量
     */
    public static long setSet(final String key, final Set<String> value, final int cacheSeconds)
    {
        long result = 0;
        Jedis jedis = null;

        try
        {
            jedis = getResource();

            jedis.del(key);

            result = jedis.sadd(key, (String[]) value.toArray());

            if (cacheSeconds != 0)
            {
                jedis.expire(key, cacheSeconds);
            }
        }
        catch (Exception e)
        {
            LOG.warn("setSet exception, key:{}", key, e);
        }
        finally
        {
            returnResource(jedis);
        }

        return result;
    }

    /**
     * 向缓存中的集合添加值
     *
     * @param key   键
     * @param value 值
     * @return 添加到缓存的元素数量
     */
    public static long addToSet(final String key, final String... value)
    {
        long result = 0;
        Jedis jedis = null;

        try
        {
            jedis = getResource();
            result = jedis.sadd(key, value);
        }
        catch (Exception e)
        {
            LOG.warn("addToSet exception, key:{}", key, e);
        }
        finally
        {
            returnResource(jedis);
        }

        return result;
    }

    /**
     * 从缓存获取映射
     *
     * @param key 键
     * @return 值
     */
    public static Map<String, String> getMap(final String key)
    {
        Map<String, String> value = null;
        Jedis jedis = null;

        try
        {
            jedis = getResource();
            value = jedis.hgetAll(key);
        }
        catch (Exception e)
        {
            LOG.warn("getMap exception, key:{}", key, e);
        }
        finally
        {
            returnResource(jedis);
        }

        return value;
    }

    /**
     * 把映射存储到缓存中
     *
     * @param key          键
     * @param value        值
     * @param cacheSeconds 超时时间，0为不超时
     * @return 添加到缓存的元素数量
     */
    public static String setMap(final String key, final Map<String, String> value, final int cacheSeconds)
    {
        String result = null;
        Jedis jedis = null;

        try
        {
            jedis = getResource();

            jedis.del(key);

            result = jedis.hmset(key, value);

            if (cacheSeconds != 0)
            {
                jedis.expire(key, cacheSeconds);
            }
        }
        catch (Exception e)
        {
            LOG.warn("setMap exception, key:{}", key, e);
        }
        finally
        {
            returnResource(jedis);
        }

        return result;
    }

    /**
     * 向缓存中的映射添加值
     *
     * @param key   键
     * @param value 值
     * @return 添加到缓存的元素数量
     */
    public static String addToMap(final String key, final Map<String, String> value)
    {
        String result = null;
        Jedis jedis = null;

        try
        {
            jedis = getResource();
            result = jedis.hmset(key, value);
        }
        catch (Exception e)
        {
            LOG.warn("addToMap exception, key:{}", key, e);
        }
        finally
        {
            returnResource(jedis);
        }

        return result;
    }

    /**
     * 从缓存中的映射移除指定的键值
     *
     * @param key   键
     * @param mapKey 值
     * @return 从缓存移除的元素数量
     */
    public static long removeFromMap(final String key, final String mapKey)
    {
        long result = 0;
        Jedis jedis = null;

        try
        {
            jedis = getResource();
            result = jedis.hdel(key, mapKey);
        }
        catch (Exception e)
        {
            LOG.warn("removeFromMap exception, key:{}", key, e);
        }
        finally
        {
            returnResource(jedis);
        }

        return result;
    }

    /**
     * 判断缓存中的映射是否存在指定的键
     *
     * @param key   键
     * @param mapKey 值
     * @return 是否存在指定的键
     */
    public static boolean mapExists(final String key, final String mapKey)
    {
        boolean result = false;
        Jedis jedis = null;

        try
        {
            jedis = getResource();
            result = jedis.hexists(key, mapKey);
        }
        catch (Exception e)
        {
            LOG.warn("mapExists exception, key:{}", key, e);
        }
        finally
        {
            returnResource(jedis);
        }

        return result;
    }

    /**
     * 从缓存删除指定的键和值
     *
     * @param key 键
     * @return 删除的键数量，0 表示键不存在
     */
    public static long del(final String key)
    {
        long result = 0;
        Jedis jedis = null;

        try
        {
            jedis = getResource();
            result = jedis.del(key);
        }
        catch (Exception e)
        {
            LOG.warn("del exception, key:{}", key, e);
        }
        finally
        {
            returnResource(jedis);
        }

        return result;
    }

    /**
     * 判断缓存中是否存在指定的键
     *
     * @param key 键
     * @return 是否存在指定的键
     */
    public static boolean exists(final String key)
    {
        boolean result = false;
        Jedis jedis = null;

        try
        {
            jedis = getResource();
            result = jedis.exists(key);
        }
        catch (Exception e)
        {
            LOG.warn("exists exception, key:{}", key, e);
        }
        finally
        {
            returnResource(jedis);
        }

        return result;
    }

    /**
     * 消息处理类
     */
    public static class MessageProcessor extends JedisPubSub
    {
        // 处理频道接收到的消息
        @Override
        public void onMessage(String channel, String message)
        {
            super.onMessage(channel, message);
        }
    }

    /**
     * 订阅频道，接收消息
     *
     * @param channel 要订阅的频道
     * @param messageProcessor 消息处理对象
     * @return Jedis 连接对象，使用完毕时需要调用 returnResource 函数归还
     */
    public static Jedis subscribe(final String channel, final MessageProcessor messageProcessor)
    {
        Jedis jedis = null;

        try
        {
            jedis = getResource();

            jedis.subscribe(messageProcessor, channel);
        }
        catch (Exception e)
        {
            LOG.warn("subscribe exception, channel{}", channel, e);
            returnResource(jedis);
            jedis = null;
        }

        return jedis;
    }

    /**
     * 从连接池获取连接
     *
     * @return 连接
     */
    public static Jedis getResource()
    {
        return jedisPool.getResource();
    }

    /**
     * 归还连接到连接池
     *
     * @param jedis 要归还的连接
     */
    public static void returnResource(final Jedis jedis)
    {
        if (jedis != null)
        {
            jedis.close();
        }
    }

    /**
     * 检查连接是否正常
     * @param jedis 连接
     * @return 连接是否正常
     */
    public static boolean isValid(final Jedis jedis)
    {
        try
        {
            return jedis != null
                    && jedis.ping().equals("PONG");
        }
        catch (Exception e)
        {
            return false;
        }
    }
}
